More on Messages and Threads

Back to Introduction to Messages
Back to Win95 ASM Page

Threads and windows

Whenever a window is created, it is "bound" to the current thread. That thread is the only thread that can execute the window procedure for that particular window. Consequently, it is the only thread that can process messages for that window. In a sense, the thread "owns" the window.
    However, another window (of the same class) can be created in another thread, allowing the two windows to process messages concurrently. Because more than one thread can be executing a given window procedure, the following discussions talk about functions being "called by the thread" instead.

Preempting or nonpreempting?

While the thread system is preemptive, the message system is not. A thread is preempted solely to let other threads run. A thread is never preempted to change its point of execution. Thus a thread, in general, won't respond immediately to new messages. [But see same-thread sends.]
    That's because message systems have three basic components:  senders, queues, and receivers. A sender routine will put a message in some designated queue, and a receiver routine must pull it out before a message handler can process it. If a receiver routine isn't called by a thread, the thread cannot handle any messages sitting in the queue. Windows provides two functions for receiving messages: GetMessage and PeekMessage.

Threads and message queues

"Posted" messages are delivered to threads. That means each thread that expects messages will have its own message queue. Because each window is associated with exactly one thread, PostMessage can determine which thread to post to (without needing a thread argument).
    "Sent" messages are, on the surface, delivered to windows. Because threads do the work of handling received messages, "sent" messages must actually be delivered to threads. Except for same-thread messages [see Same-thread sends], such messages must be placed in a thread message queue -- one that is associated with the destination window. Whether or not this queue is the same as the "PostMessage" queue is unimportant. It's only important for the system to know if a message is from a "SendMessage" function or a "PostMessage" function.

Thread blocking and SendMessage

When sending a message to a window, the sending function may need to wait for a reply. While it is waiting, the thread which called the function is said to be blocked. This situation prevents the thread from handling any messages for other windows it "owns". Thus window contents don't get updated, and when a window is uncovered, it doesn't get redrawn.
    When SendMessage sends a message to a window in another thread, it waits for a reply which is issued when the receiving thread calls ReplyMessage. An explicit reply is usually not necessary because when a window procedure exits, ReplyMessage is automatically called. An explicit ReplyMessage will be necessary to avoid deadlock if there is chain of unfinished SendMessage calls that involves more than one thread in a circular fashion. [See Deadlock Theory.]
    For example:

    The SendMessage chain

        (window A, thread 1) --> (B, thread 2) --> ... --> (C, thread n) --> (D, [back to] thread 1)

    will deadlock when C (in thread n) sends a message to D, if none of the window procedures have called ReplyMessage. If any of the invoked window procedures (other than D) call ReplyMessage before calling SendMessage, the chain is broken.

Same-thread sends

SendMessage, a thread blocking function, is used quite often to send a message to a window in the same thread. Under normal rules, this function would put the message on a queue and then force the thread to wait for itself to reply.  Since it's waiting, it can't reply--deadlock. In this special case, a "same-thread" send, Windows will directly call the proper window procedure as if it were a subroutine. In effect, this is thread preemption for the purpose of responding to new messages.
    Because it doesn't require the message to be pulled out of a queue, this behavior allows messages to be processed without a message loop. For example, CreateWindowEx can invoke your special WM_CREATE code, and draw the window frame before your app enters its message loop. Also, any window (created in the same thread) can be updated immediately by sending it the appropriate message.
    But be aware that this immediate response occurs only when a message is sent (with SendMessage) to a window in the same thread.

Forms of message transmission

SendMessage is a thread blocking function. It always waits for a reply.
    SendMessageTimeout blocks like SendMessage, but it unblocks if the receiver takes too long to reply. You specify the timeout interval.
    SendNotifyMessage is a nonblocking function. It doesn't wait for a reply, unless the destination window is in the same thread.
    SendMessageCallback is another nonblocking function. It also doesn't wait for a reply, unless the destination window is in the same thread. You specify a callback routine which will be executed when the receiving thread replies.
    PostMessage is completely nonblocking. It never waits for a reply. You must target a window. The window determines which thread will receive the message.
    PostThreadMessage is nonblocking and can't target a window. You must target a thread.
    PostQuitMessage is nonblocking and posts a WM_QUIT message to the current thread.